#include "login_module.h"

#include <boost/asio.hpp>
using namespace boost;
using namespace boost::asio;

#include <common/log/log.h>
#include <common/net/message_parse.h>
#include <common/net/session.h>

#include <protocols/protos/common.pb.h>
using namespace proto::common;
#include <protocols/protos/db.pb.h>
#include <protocols/protos/login.pb.h>
using namespace proto::login;
#include <protocols/protos/tables.pb.h>
using namespace proto::tables;

#include <common/base/module_mgr.h>
#include <common/utils/util.h>

#include "base/account_mgr.h"
#include "base/player.h"
#include "base/player_mgr.h"
#include "base/sub_server_mgr.h"
#include "net/custom_data.h"

bool LoginModule::doDispatch(const std::shared_ptr<SubServerMgr>& submgr,
                             const std::shared_ptr<Player>& player,
                             const MessageID& msgID,
                             const char* buf,
                             std::size_t bufSize)
{
    LOG_WARN("doDispatch not implement.");
    return false;
}

bool LoginModule::doDispatch(const std::shared_ptr<Session>& session,
                             const MessageID& msgID,
                             const char* buf,
                             std::size_t bufSize)
{
    switch (msgID.stMsg.msgID)
    {
    case C2S_LOGIN_REQ:
        return onLoginReq(session, msgID, buf, bufSize);

    case C2S_CREATE_PLAYER_REQ:
        return onCreatePlayerReq(session, msgID, buf, bufSize);

    case C2S_CHOOSE_PLAYER_REQ:
        return onChoosePlayerReq(session, msgID, buf, bufSize);

    case C2S_LOGIN_CONFIRM_REQ:
        return onLoginConfirm(session, msgID, buf, bufSize);

    default:
        return false;
    }

    return false;
}

bool LoginModule::onLoginReq(const std::shared_ptr<Session>& session,
                             const MessageID& msgID,
                             const char* buf,
                             std::size_t bufSize)
{
    LoginReq req;
    if (!req.ParseFromArray(buf, bufSize))
    {
        LOG_ERROR("Parse proto data failed.");
        return false;
    }
    LOG_DEBUG("Got login req, acc_name: " << req.acc_name());

    const auto& subMgr = ServerMgr::get().getSubServ<SubServerMgr>();
    if (!subMgr)
    {
        LOG_ERROR("no sub manager was found.");
        return false;
    }

    LoginRet ret;
    ret.set_err(eFAILED);
    auto accMgr = subMgr->getSubMgr<AccountMgr>(SubManagerType::eAccount);
    auto accInfo = accMgr->getAccInfo(req.acc_name(), req.source());
    if (!accInfo)
    { // 没有帐号，创建新的帐号，并生成新的随机名用于创建新的角色
        uint32_t accID = 0;
        if (!accMgr->createAccount(req.acc_name(), req.source(), accID))
        {
            LOG_ERROR("Add account failed, acc name:" << req.acc_name()
                      << " source:" << req.source());
            return false;
        }
        ret.set_err(eCREATE_NEW_PLAYER);
        ret.set_acc_id(accID);
        ret.set_rand_name(accMgr->genRandName());
        session->sendMsg(proto::common::eLogin, proto::login::S2C_LOGIN_RET, ret);
        return true;
    }

    if (accInfo->curLoginSession)
    { // 已经登录，等待客户端确认退出
        ret.set_err(eALREADY_LOGIN);
        ret.set_acc_id(accInfo->accID);
        session->sendMsg(proto::common::eLogin, proto::login::S2C_LOGIN_RET, ret);
        return true;
    }
    return retPlayerList(session, accMgr, accInfo->accID);
}

bool LoginModule::onCreatePlayerReq(const std::shared_ptr<Session>& session,
                                    const MessageID& msgID,
                                    const char* buf,
                                    std::size_t bufSize)
{
    CreatePlayerReq req;
    if (!req.ParseFromArray(buf, bufSize))
    {
        LOG_ERROR("Parse proto data failed.");
        return false;
    }
    LOG_DEBUG("Got create player req, palyer_name: " << req.player_name());

    const auto& subMgr = ServerMgr::get().getSubServ<SubServerMgr>();
    if (!subMgr)
    {
        LOG_ERROR("no sub manager was found.");
        return false;
    }

    CreatePlayerRet ret;
    ret.set_err(eFAILED);
    auto accMgr = subMgr->getSubMgr<AccountMgr>(SubManagerType::eAccount);
    uint32_t playerID = 0;
    if (!accMgr->createPlayer(req.player_name(), req.acc_id(), req.head_id(), playerID))
    {
        LOG_ERROR("create player failed. player name: " << req.player_name()
                  << " acc id: " << req.acc_id());
        session->sendMsg(proto::common::eLogin, proto::login::S2C_CREATE_PLAYER_RET, ret);
        return false;
    }
    ret.set_err(eSUC);
    ret.set_acc_id(req.acc_id());
    ret.set_player_id(playerID);
    session->sendMsg(proto::common::eLogin, proto::login::S2C_CREATE_PLAYER_RET, ret);
    return true;
}

bool LoginModule::onChoosePlayerReq(const std::shared_ptr<Session>& session,
                                 const MessageID& msgID,
                                 const char* buf,
                                 std::size_t bufSize)
{
    ChoosePlayerReq req;
    if (!req.ParseFromArray(buf, bufSize))
    {
        LOG_ERROR("Parse proto data failed.");
        return false;
    }

    const auto& subMgr = ServerMgr::get().getSubServ<SubServerMgr>();
    if (!subMgr)
    {
        LOG_ERROR("no sub manager was found.");
        return false;
    }

    // ChoosePlayerRet ret;
    // ret.set_err(eFAILED);
    auto accMgr = subMgr->getSubMgr<AccountMgr>(SubManagerType::eAccount);
    auto accInfo = accMgr->getAccInfo(req.acc_id());
    if (!accInfo)
    {
        LOG_ERROR("no account was found. acc_id:" <<req.acc_id());
        return false;
    }

    auto playerMgr = subMgr->getSubMgr<PlayerMgr>(SubManagerType::ePlayer);
    if (!playerMgr)
    {
        LOG_ERROR("No player manager was found");
        return false;
    }

    auto player = playerMgr->getPlayer(req.player_id());
    if (!player)
    {
        player = std::make_shared<Player>(req.player_id());
    }
    player->setCltSession(session);
    auto gsSession = player->getGsSession();
    if (!gsSession)
    {
        gsSession = accMgr->chooseGsSession(session, req.player_id());
    }
    if (!gsSession)
    {
        LOG_ERROR("gs session not found.");
        return false;
    }
    player->setGsSession(gsSession);
    playerMgr->addPlayer(player);
    auto& cltCustom = getCustom<CltSessionCustomData>(session);
    cltCustom.setPlayer(player);

    auto moduleMgr = ServerMgr::get().getMgr<ModuleMgr>(ManagerType::eModule);
    if (!moduleMgr)
    {
        LOG_ERROR("No found module manager.");
        return false;
    }
    const auto& module = moduleMgr->getModule(proto::common::eCommon);
    if (!module)
    {
        LOG_ERROR("common module not init.");
        return false;
    }
    accInfo->curLoginSession = session;
    return module->doTranslate(session, msgID, buf, bufSize);
}

bool LoginModule::onLoginConfirm(const std::shared_ptr<Session>& session,
                                 const MessageID& msgID,
                                 const char* buf,
                                 std::size_t bufSize)
{
    LoginConfirmReq req;
    if (!req.ParseFromArray(buf, bufSize))
    {
        LOG_ERROR("Parse proto data failed.");
        return false;
    }

    const auto& subMgr = ServerMgr::get().getSubServ<SubServerMgr>();
    if (!subMgr)
    {
        LOG_ERROR("no sub manager was found.");
        return false;
    }

    auto accMgr = subMgr->getSubMgr<AccountMgr>(SubManagerType::eAccount);
    auto accInfo = accMgr->getAccInfo(req.acc_id());
    if (accInfo && accInfo->curLoginSession)
    {
        if (session == accInfo->curLoginSession)
        {
            LOG_DEBUG("same session has login. ");
            return true;
        }
        LoginConfirmRet ret;
        accInfo->curLoginSession->sendMsg(proto::common::eLogin, proto::login::S2C_LOGIN_CONFIRM_RET, ret);
        sleep(1);
        accInfo->curLoginSession->stop();
        return true;
    }
    return retPlayerList(session, accMgr, req.acc_id());
}

bool LoginModule::retPlayerList(const std::shared_ptr<Session>& session,
                                const std::shared_ptr<AccountMgr>& accMgr,
                                uint32_t accID)
{
    // 可以登录，返回角色列表，等待用户选择
    LoginRet ret;
    ret.set_err(eFAILED);
    ret.set_acc_id(accID);
    std::vector<PlayerCreateInfo> players;
    accMgr->getPlayerList(accID, players);
    if (players.empty())
    { // 只有帐号没有角色， 生成新的随机名用于创建新的角色
        ret.set_err(eCREATE_NEW_PLAYER);
        ret.set_rand_name(accMgr->genRandName());
        session->sendMsg(proto::common::eLogin, proto::login::S2C_LOGIN_RET, ret);
        return true;
    }
    for (auto &player : players)
    {
        ret.set_err(eSUC);
        auto playerInfo = ret.add_players();
        playerInfo->set_player_id(player.playerID);
        playerInfo->set_player_name(player.playerName);
        playerInfo->set_head_id(player.headID);
        playerInfo->set_last_login_time(player.lastLoginTime);
    }
    session->sendMsg(proto::common::eLogin, proto::login::S2C_LOGIN_RET, ret);
    return true;
}
